#include "esp_light_main.h"

// 将一个字节的值转换为两位的十六进制字符串
String byteToHexString(uint8_t value)
{
  char buffer[3];
  snprintf(buffer, sizeof(buffer), "%02X", value);
  return String(buffer);
}

void s1_tic()
{

  if (WiFi.status() == WL_CONNECTED)
  {
    // 灯状态的更新  // 按键触发开关灯，灯状态的更新
    for (byte i = 0; i < LIGHT_NUMBER; i++)
    {
      if (i == 0)
      {
        if (CONTROL_STAT[i])
        { // blinker按键状态更新
          BButton1.print("off");
        }
        else
        {
          BButton1.print("on");
        }
      }
      else if (i == 1)
      {
        if (CONTROL_STAT[i])
        { // blinker按键状态更新
          BButton2.print("off");
        }
        else
        {
          BButton2.print("on");
        }
      }
      else if (i == 2)
      {
        if (CONTROL_STAT[i])
        {
          BButton3.print("off");
        }
        else
        {
          BButton3.print("on");
        }
      }
      else if (i == 3)
      {
        if (CONTROL_STAT[i])
        {
          BButton4.print("off");
        }
        else
        {
          BButton4.print("on");
        }
      }
    }
  }
}

void Ticker_init()
{
  ticker1s.attach(1, s1_tic); // 1s定时器
}

void click(Button2 &btn)
{
  if (btn == buttonA)
  {
    Serial.println("A clicked");
    touch_touched[0] = true;
  }
  else if (btn == buttonB)
  {
    Serial.println("B clicked");
    touch_touched[1] = true;
  }
  else if (btn == buttonC)
  {
    Serial.println("C clicked");
    touch_touched[2] = true;
  }
  else if (btn == buttonD)
  {
    Serial.println("D clicked");
    touch_touched[3] = true;
  }
}

void dataRead(const String &data)
{
  BLINKER_LOG("Blinker readString: ", data);
}

void heartbeat()
{
}

void button1_callback(const String &state)
{
  BLINKER_LOG("get button1 state: ", state);
  touch_touched[0] = true;
}

void button2_callback(const String &state)
{
  BLINKER_LOG("get button2 state: ", state);
  touch_touched[1] = true;
}

void button3_callback(const String &state)
{
  BLINKER_LOG("get button3 state: ", state);
  touch_touched[2] = true;
}

void button4_callback(const String &state)
{
  BLINKER_LOG("get button4 state: ", state);
  touch_touched[3] = true;
}

// 天猫精灵，小米米家语音命令处理
bool oState[5] = {false};
void blinkerPowerState(const String &state, uint8_t num)
{
  BLINKER_LOG("need set outlet: ", num, "power state: ", state);

  if (state == BLINKER_CMD_ON)
  {
    if (num <4)
    {
      CONTROL_STAT[num] = 0;
      touch_touched[num] = true;

      BlinkerAliGenie.powerState("on", num);
      BlinkerAliGenie.print();

      BlinkerMIOT.powerState("on", num);
      BlinkerMIOT.print();

      oState[num] = true;
    }else// 打开所有的设备
    {
      for (uint8_t o_num = 0; o_num < 5; o_num++)
      {
        CONTROL_STAT[o_num] = 1;
        touch_touched[o_num] = true;

        BlinkerAliGenie.powerState("on", o_num);
        BlinkerAliGenie.print();

        BlinkerMIOT.powerState("on", o_num);
        BlinkerMIOT.print();
        oState[o_num] = true;
      }
    }
  }
  else if (state == BLINKER_CMD_OFF)
  {
    if (num <4)
    {
      CONTROL_STAT[num] = 1;
      touch_touched[num] = true;

      BlinkerAliGenie.powerState("off", num);
      BlinkerAliGenie.print();

      BlinkerMIOT.powerState("off", num);
      BlinkerMIOT.print();

      oState[num] = false;
    }else// 关闭所有的设备
    {
      for (uint8_t o_num = 0; o_num < 5; o_num++)
      {
        CONTROL_STAT[o_num] = 0;
        touch_touched[o_num] = true;

        BlinkerAliGenie.powerState("off", o_num);
        BlinkerAliGenie.print();

        BlinkerMIOT.powerState("off", o_num);
        BlinkerMIOT.print();
        oState[o_num] = false;
      }
    }
  }
}

void blinkerQuery(int32_t queryCode, uint8_t num)
{
  BLINKER_LOG("blinker Query outlet: ", num, ", codes: ", queryCode);

  switch (queryCode)
  {
  case BLINKER_CMD_QUERY_ALL_NUMBER:
    BLINKER_LOG("Blinker Query All");
    BlinkerAliGenie.powerState(oState[num] ? "on" : "off", num);
    BlinkerAliGenie.print();
    BlinkerMIOT.powerState(oState[num] ? "on" : "off", num);
    BlinkerMIOT.print();
    break;
  case BLINKER_CMD_QUERY_POWERSTATE_NUMBER:
    BLINKER_LOG("Blinker Query Power State");
    BlinkerAliGenie.powerState(oState[num] ? "on" : "off", num);
    BlinkerAliGenie.print();
    BlinkerMIOT.powerState(oState[num] ? "on" : "off", num);
    BlinkerMIOT.print();
    break;
  default:
    BlinkerAliGenie.powerState(oState[num] ? "on" : "off", num);
    BlinkerAliGenie.print();
    BlinkerMIOT.powerState(oState[num] ? "on" : "off", num);
    BlinkerMIOT.print();
    break;
  }
}

void setup()
{
  delay(50);
  // Wire.begin(SDA_PIN, SCL_PIN);

  Serial.begin(115200);
  BLINKER_DEBUG.stream(Serial);
  Serial.println("V1.04");
  loadDefaultConfig(); // 载入默认配置

  RelayPin[0] = LIGHT_1_PIN;
  RelayPin[1] = LIGHT_2_PIN;
  RelayPin[2] = LIGHT_3_PIN;
  RelayPin[3] = LIGHT_4_PIN;
  // 磁保持继电器需要使用2个IO进行控制
  RelayPin2[0] = LIGHT_3B_PIN;
  RelayPin2[1] = LIGHT_4B_PIN;

  // D1 D2初始电平低，设置为高电平开灯，其他脚位设置为低电平开灯
  pinMode(RelayPin[0], OUTPUT);
  pinMode(RelayPin[1], OUTPUT);
  digitalWrite(RelayPin[0], LOW); // 高电平有效
  digitalWrite(RelayPin[1], LOW); // 高电平有效

  // 磁保持继电器初始化 保持关闭 由于立创引脚图错误，导致开关颠倒，这里做反向处理
  // 磁保持继电器A 高电平，B低电平开启，A低电平，B高电平关闭 磁保持继电器上电复位>50ms 100ms脉冲触发
  // 此处反向输出，所以A为低电平有效，B为高电平有效
  // 磁保持上电复位>50ms，默认关闭  此组高电平有效，低电平无效
  pinMode(RelayPin[2], OUTPUT);
  digitalWrite(RelayPin[2], HIGH);
  pinMode(RelayPin2[0], OUTPUT);
  digitalWrite(RelayPin2[0], LOW);

  // 磁保持上电复位>50ms，默认关闭  此组低电平有效，高电平无效
  pinMode(RelayPin[3], OUTPUT);
  digitalWrite(RelayPin[3], LOW);
  pinMode(RelayPin2[1], OUTPUT);
  digitalWrite(RelayPin2[1], HIGH);

  delay(200);

  digitalWrite(RelayPin[2], HIGH);
  digitalWrite(RelayPin2[0], HIGH);

  digitalWrite(RelayPin[3], HIGH);
  digitalWrite(RelayPin2[1], HIGH);

  // 按键
  CONTROL_PIN[0] = CONTROL_1_PIN;
  CONTROL_PIN[1] = CONTROL_2_PIN;
  CONTROL_PIN[2] = CONTROL_3_PIN;
  CONTROL_PIN[3] = CONTROL_4_PIN;

  buttonA.begin(CONTROL_PIN[0]);
  buttonA.setReleasedHandler(click);

  buttonB.begin(CONTROL_PIN[1]);
  buttonB.setReleasedHandler(click);

  buttonC.begin(CONTROL_PIN[2]);
  buttonC.setReleasedHandler(click);

  buttonD.begin(CONTROL_PIN[3]);
  buttonD.setReleasedHandler(click);

  for (byte i = 0; i < LIGHT_NUMBER; i++)
  {
    // 外部开关 低电平触发开
    pinMode(CONTROL_PIN[i], INPUT_PULLUP); // 设置按键管脚上拉输入模式

    // 默认操作完成则初始化开关状态值
    CONTROL_STAT[i] = digitalRead(CONTROL_PIN[i]);
    Serial.println(CONTROL_STAT[i]);
  }

  byte mac[6];
  WiFi.macAddress(mac);
  WiFi.setHostname(ServerName);
  printf("macAddress 0x%02X:0x%02X:0x%02X:0x%02X:0x%02X:0x%02X\r\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);

  // 构建新的AP的SSID
  apSSID = DEVICE_NAME + String("_") + byteToHexString(mac[3]) + byteToHexString(mac[4]) + byteToHexString(mac[5]);

  // 连接WiFi
  WiFi.disconnect(true);
  WiFi.setHostname((const char *)apSSID.c_str());

  WiFi.setAutoReconnect(true);
  WiFi.persistent(false);

  Serial.println("WiFi AutoConfig Waiting......");
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
    Serial.print(".");
    // 尝试连接AP
    if (WiFi.begin((const char *)config_json["STA_ssid"], (const char *)config_json["STA_passwd"]))
    {
      Serial.println("WiFi connected");
      break;
    }
  }

  // 连接成功后，获取IP地址
  IPAddress myIP = WiFi.localIP();
  Serial.print("My IP address: ");
  Serial.println(myIP);

  if (strlen(blinker_id) == 12 && (0 != strcmp("000000000000", blinker_id)))
  {
    Serial.println("blinker start!");
    // 初始化blinker
    Blinker.begin(blinker_id, (const char *)config_json["STA_ssid"], (const char *)config_json["STA_passwd"]);

    BlinkerAliGenie.attachPowerState(blinkerPowerState);
    BlinkerAliGenie.attachQuery(blinkerQuery);

    BlinkerMIOT.attachPowerState(blinkerPowerState);
    BlinkerMIOT.attachQuery(blinkerQuery);

    Blinker.attachData(dataRead);
    // Blinker.attachHeartbeat(heartbeat);
    BButton1.attach(button1_callback);
    BButton2.attach(button2_callback);
    BButton3.attach(button3_callback);
    BButton4.attach(button4_callback);
  }
  else
  {
    Serial.println("Blinker id error!");
  }

  // 以下是启动OTA，可以通过WiFi刷新固件
  ArduinoOTA.setHostname((const char *)apSSID.c_str());
  // No authentication by default
  // ArduinoOTA.setPassword("admin");

  // Password can be set with it's md5 value as well
  // MD5(admin) = 21232f297a57a5a743894a0e4a801fc3
  ArduinoOTA.setPasswordHash("21232f297a57a5a743894a0e4a801fc3");
  ArduinoOTA.onStart([]()
                     {
        String type;
        if (ArduinoOTA.getCommand() == U_FLASH) {
          type = "sketch";
        } else { // U_SPIFFS
          type = "filesystem";
        }

        // NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
        Serial.println("Start updating " + type); });
  ArduinoOTA.onEnd([]()
                   { Serial.println("\nEnd"); });
  ArduinoOTA.onProgress([](unsigned int progress, unsigned int total)
                        { Serial.printf("Progress: %u%%\r", (progress / (total / 100))); });
  ArduinoOTA.onError([](ota_error_t error)
                     {
        Serial.printf("Error[%u]: ", error);
        if (error == OTA_AUTH_ERROR) {
          Serial.println("Auth Failed");
        } else if (error == OTA_BEGIN_ERROR) {
          Serial.println("Begin Failed");
        } else if (error == OTA_CONNECT_ERROR) {
          Serial.println("Connect Failed");
        } else if (error == OTA_RECEIVE_ERROR) {
          Serial.println("Receive Failed");
        } else if (error == OTA_END_ERROR) {
          Serial.println("End Failed");
        } });
  ArduinoOTA.begin();

  Ticker_init(); // 定时器配置

  Serial.println();
  Serial.println("Client ready");
  Serial.println("-----------------------------------------------");
}

void loop()
{

  ArduinoOTA.handle();
  Blinker.run();

  buttonA.loop();
  buttonB.loop();
  buttonC.loop();
  buttonD.loop();

  // 按键触发开关灯，灯状态的更新
  for (byte i = 0; i < LIGHT_NUMBER; i++)
  {
    if (touch_touched[i])
    {                           // 按过的处理
      touch_touched[i] = false; // 重置按键状态

      Serial.print("\n");
      Serial.print(i);
      Serial.println(" Light touched");

      CONTROL_STAT[i] = !CONTROL_STAT[i]; // 翻转开关状态

      // 1,2高电平开启 3,4磁保持无状态，无法读取io来作为状态，依靠保存的状态来处理
      if (i == 0)
      {
        digitalWrite(RelayPin[i], !CONTROL_STAT[i]);
        if (CONTROL_STAT[i])
        { // blinker按键状态更新
          BButton1.print("off");
        }
        else
        {
          BButton1.print("on");
        }
      }
      else if (i == 1)
      {
        digitalWrite(RelayPin[i], !CONTROL_STAT[i]);
        if (CONTROL_STAT[i])
        { // blinker按键状态更新
          BButton2.print("off");
        }
        else
        {
          BButton2.print("on");
        }
      }
      else if (i == 2)
      {
        if (CONTROL_STAT[i])
        {
          digitalWrite(RelayPin[2], HIGH);
          digitalWrite(RelayPin2[0], LOW);
          delay(200);
          digitalWrite(RelayPin[2], HIGH);
          digitalWrite(RelayPin2[0], HIGH);
          // blinker按键状态更新
          BButton3.print("off");
        }
        else
        {
          digitalWrite(RelayPin[2], LOW);
          digitalWrite(RelayPin2[0], HIGH);
          delay(200);
          digitalWrite(RelayPin[2], HIGH);
          digitalWrite(RelayPin2[0], HIGH);
          BButton3.print("on");
        }
      }
      else if (i == 3)
      {
        if (CONTROL_STAT[i])
        {
          digitalWrite(RelayPin[3], LOW);
          digitalWrite(RelayPin2[1], HIGH);
          delay(200);
          digitalWrite(RelayPin[3], HIGH);
          digitalWrite(RelayPin2[1], HIGH);
          // blinker按键状态更新
          BButton4.print("off");
        }
        else
        {
          digitalWrite(RelayPin[3], HIGH);
          digitalWrite(RelayPin2[1], LOW);
          delay(200);
          digitalWrite(RelayPin[3], HIGH);
          digitalWrite(RelayPin2[1], HIGH);
          BButton4.print("on");
        }
      }
    }
  }
}
